using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using SageKing.Designer.Core.Models;
using SageKing.Designer.Core;
using SageKing.Designer.Core.Activities.Flowchart.Models;
using SageKing.Designer.Core.Attributes;
using SageKing.Designer.Core.Contracts; 
using JetBrains.Annotations;

namespace SageKing.Designer.Core.Activities;

/// <summary>
/// Represents a composite activity that has a single <see cref="Root"/> activity. Like a workflow, but without workflow-level properties.
/// </summary>
[PublicAPI]
public abstract class Composite : Activity, IVariableContainer
{
    /// <inheritdoc />
    protected Composite(string? source = default, int? line = default) : base(source, line)
    {
        
    }

    /// <inheritdoc />
    [JsonIgnore]  // Composite activities' Variables is intended to be constructed from code only.
    public ICollection<Variable> Variables { get; init; } = new List<Variable>();

    /// <summary>
    /// A variable to allow activities to set a result.
    /// </summary>
    [JsonIgnore]
    public Variable? ResultVariable { get; set; }
    
    /// <summary>
    /// The activity to schedule when this activity executes.
    /// </summary>
    [Port]
    [Browsable(false)]
    [JsonIgnoreCompositeRoot]
    public IActivity Root { get; set; } = new Sequence();

    
    /// <summary>
    /// Creates a new <see cref="Activities.Inline"/> activity.
    /// </summary>
    protected static Inline Inline(Func<ValueTask> activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(activity, source, line);
     
    
    /// <summary>
    /// Creates a new <see cref="Activities.Inline"/> activity.
    /// </summary>
    protected static Inline Inline(Action activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(activity, source, line);
     
    /// <summary>
    /// Creates a new <see cref="Activities.Inline"/> activity.
    /// </summary>
    protected static Inline<TResult> Inline<TResult>(Func<ValueTask<TResult>> activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(activity, output, source, line);
     
    /// <summary>
    /// Creates a new <see cref="Activities.Inline"/> activity.
    /// </summary>
    protected static Inline<TResult> Inline<TResult>(Func<TResult> activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(activity, output, source, line);

    /// <summary>
    /// Creates a new <see cref="Activities.SetVariable"/> activity.
    /// </summary>
    protected static SetVariable<T> SetVariable<T>(Variable<T> variable, T value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(variable, value, source, line);
    
    /// <summary>
    /// Creates a new <see cref="Activities.SetVariable"/> activity.
    /// </summary>
    protected static SetVariable<T> SetVariable<T>(Variable<T> variable, Func<ExpressionExecutionContext, T> value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(variable, value, source, line);
    
    /// <summary>
    /// Creates a new <see cref="Activities.SetVariable"/> activity.
    /// </summary>
    protected static SetVariable<T> SetVariable<T>(Variable<T> variable, Func<T> value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(variable, value, source, line);
    
    /// <summary>
    /// Creates a new <see cref="Activities.SetVariable"/> activity.
    /// </summary>
    protected static SetVariable<T> SetVariable<T>(Variable<T> variable, Variable<T> value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) => new(variable, value, source, line);
}

/// <summary>
/// Base class for custom activities with auto-complete behavior that return a result.
/// </summary>
[PublicAPI]
public abstract class CompositeWithResult : Composite
{
    /// <inheritdoc />
    protected CompositeWithResult(string? source = default, int? line = default) : base(source, line)
    {
    }

    /// <inheritdoc />
    protected CompositeWithResult(MemoryBlockReference? output, string? source = default, int? line = default) : base(source, line)
    {
        if (output != null) Result = new Output(output);
    }

    /// <inheritdoc />
    protected CompositeWithResult(Output? output, string? source = default, int? line = default) : base(source, line)
    {
        Result = output;
    }

    /// <summary>
    /// The result value of the composite.
    /// </summary>
    [Output] public Output? Result { get; set; }
}

/// <summary>
/// Represents a composite activity that has a single <see cref="Composite.Root"/> activity and returns a result.
/// </summary>
[PublicAPI]
public abstract class Composite<T> : Composite, IActivityWithResult<T>
{
    /// <inheritdoc />
    protected Composite([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
    {
    }

    /// <summary>
    /// The result of the activity.
    /// </summary>
    [Output] public Output<T>? Result { get; set; }

    Output? IActivityWithResult.Result
    {
        get => Result;
        set => Result = (Output<T>?)value;
    }
}